#include <sdk.h>
#include "sc_plugin.h"
#include <manager.h>
#include <scriptingmanager.h>
#include <wx/menu.h>
#include <map>

namespace ScriptBindings {
  namespace ScriptPluginWrapper {
    struct MenuCallback {
      SquirrelObject object;
      int menuIndex;
    };
    typedef std::map<int, MenuCallback> ModuleMenuCallbacks;
    ModuleMenuCallbacks s_MenuCallbacks;
    typedef std::map<wxString, SquirrelObject> ScriptPlugins;
    ScriptPlugins s_ScriptPlugins;
    typedef std::map<wxString, MenuItemsManager> ScriptPluginsMenus;
    ScriptPluginsMenus s_ScriptPluginsMenus;
    wxArrayInt CreateMenu( const wxString& name ) {
      wxArrayInt ret;
      ScriptPlugins::iterator it = s_ScriptPlugins.find( name );
      if( it == s_ScriptPlugins.end() ) {
        return ret;
      }
      SquirrelObject& o = it->second;
      ScriptPluginsMenus::iterator itm = s_ScriptPluginsMenus.find( name );
      if( itm == s_ScriptPluginsMenus.end() ) {
        itm = s_ScriptPluginsMenus.insert( s_ScriptPluginsMenus.end(), std::make_pair( name, MenuItemsManager( false ) ) );
      }
      MenuItemsManager& mi = itm->second;
      SqPlus::SquirrelFunction<wxArrayString&> f( o, "GetMenu" );
      if( f.func.IsNull() ) {
        return ret;
      }
      wxArrayString arr;
      try {
        arr = f();
      } catch( SquirrelError e ) {
        msging( cbC2U( e.desc ), _T( "脚本错误" ), wxICON_ERROR );
        return ret;
      }
      if( arr.GetCount() ) {
        for( size_t i = 0; i < arr.GetCount(); ++i ) {
          int id = wxNewId();
          id = mi.CreateFromString( arr[i], id );
          ret.Add( id );
          MenuCallback callback;
          callback.object = it->second;
          callback.menuIndex = i;
          ModuleMenuCallbacks::iterator mmcIt = s_MenuCallbacks.find( id );
          if( mmcIt == s_MenuCallbacks.end() ) {
            s_MenuCallbacks.insert( s_MenuCallbacks.end(), std::make_pair( id, callback ) );
          } else {
            s_MenuCallbacks.erase( mmcIt );
            s_MenuCallbacks.insert( s_MenuCallbacks.end(), std::make_pair( id, callback ) );
          }
        }
      }
      return ret;
    }
    wxArrayInt CreateModuleMenu( const ModuleType typ, wxMenu* menu, const FileTreeData* data ) {
      wxArrayInt ret;
      ScriptPlugins::iterator it;
      for( it = s_ScriptPlugins.begin(); it != s_ScriptPlugins.end(); ++it ) {
        SquirrelObject& o = it->second;
        SqPlus::SquirrelFunction<wxArrayString&> f( o, "GetModuleMenu" );
        if( f.func.IsNull() ) {
          continue;
        }
        wxArrayString arr;
        try {
          arr = f( typ, data );
        } catch( SquirrelError e ) {
          msging( cbC2U( e.desc ), _T( "脚本错误" ), wxICON_ERROR );
          continue;
        }
        if( arr.GetCount() == 1 ) {
          int id = wxNewId();
          menu->Append( id, arr[0] );
          ret.Add( id );
          MenuCallback callback;
          callback.object = it->second;
          callback.menuIndex = 0;
          s_MenuCallbacks.insert( s_MenuCallbacks.end(), std::make_pair( id, callback ) );
        } else if( arr.GetCount() > 1 ) {
          wxMenu* sub = new wxMenu;
          for( size_t i = 0; i < arr.GetCount(); ++i ) {
            int id = wxNewId();
            sub->Append( id, arr[i] );
            ret.Add( id );
            MenuCallback callback;
            callback.object = it->second;
            callback.menuIndex = i;
            s_MenuCallbacks.insert( s_MenuCallbacks.end(), std::make_pair( id, callback ) );
          }
          menu->Append( -1, it->first, sub );
        }
      }
      return ret;
    }
    void OnScriptMenu( int id ) {
      ModuleMenuCallbacks::iterator it;
      it = s_MenuCallbacks.find( id );
      if( it != s_MenuCallbacks.end() ) {
        MenuCallback& callback = it->second;
        SqPlus::SquirrelFunction<void> f( callback.object, "OnMenuClicked" );
        if( !f.func.IsNull() ) {
          try {
            f( callback.menuIndex );
          } catch( SquirrelError e ) {
            msging( cbC2U( e.desc ), _T( "脚本错误" ), wxICON_ERROR );
          }
        }
      }
    }
    void OnScriptModuleMenu( int id ) {
      ModuleMenuCallbacks::iterator it;
      it = s_MenuCallbacks.find( id );
      if( it != s_MenuCallbacks.end() ) {
        MenuCallback& callback = it->second;
        SqPlus::SquirrelFunction<void> f( callback.object, "OnModuleMenuClicked" );
        if( !f.func.IsNull() ) {
          try {
            f( callback.menuIndex );
          } catch( SquirrelError e ) {
            msging( cbC2U( e.desc ), _T( "脚本错误" ), wxICON_ERROR );
          }
        }
      }
    }
    SQInteger RegisterPlugin( HSQUIRRELVM v ) {
      SquirrelObject o;
      o.AttachToStackObject( 2 );
      if( !o.Exists( "GetPluginInfo" ) ) {
        return sq_throwerror( v, "不是脚本插件!" );
      }
      SqPlus::SquirrelFunction<PluginInfo&> f( o, "GetPluginInfo" );
      PluginInfo& info = f();
      wxString s = info.name;
      ScriptPlugins::iterator it = s_ScriptPlugins.find( s );
      if( it != s_ScriptPlugins.end() ) {
        s_ScriptPlugins.erase( it );
        Manager::Get()->GetLogManager()->Log( _T( "脚本插件已注册: " ) + s );
      }
      it = s_ScriptPlugins.insert( s_ScriptPlugins.end(), std::make_pair( s, o ) );
      Manager::Get()->GetLogManager()->Log( _T( "脚本插件已注册: " ) + s );
      Manager::Get()->GetScriptingManager()->RegisterScriptPlugin( s, CreateMenu( s ) );
      return 0;
    }
    SQInteger GetPlugin( HSQUIRRELVM v ) {
      StackHandler sa( v );
      const wxString& name = *SqPlus::GetInstance<wxString, false>( v, 2 );
      ScriptPlugins::iterator it = s_ScriptPlugins.find( name );
      if( it != s_ScriptPlugins.end() ) {
        return sa.Return( it->second );
      }
      return sa.Return();
    }
    int ExecutePlugin( const wxString& name ) {
      ScriptPlugins::iterator it = s_ScriptPlugins.find( name );
      if( it != s_ScriptPlugins.end() ) {
        SquirrelObject& o = it->second;
        SqPlus::SquirrelFunction<int> f( o, "Execute" );
        if( !f.func.IsNull() ) {
          try {
            f();
          } catch( SquirrelError e ) {
            msging( cbC2U( e.desc ), _T( "脚本错误" ), wxICON_ERROR );
          }
        }
      }
      return -1;
    }

  };
  const char* s_cbScriptPlugin =
    "class cbScriptPlugin {\n"
    "    info = PluginInfo();\n"
    "    constructor() {\n"
    "        info.name = _T(\"cbScriptPlugin\");\n"
    "        info.title = _T(\"基本脚本插件\");\n"
    "    }\n"
    "    function GetPluginInfo() {\n"
    "        return info;\n"
    "    }\n"
    "    function GetMenu() {\n"
    "        return wxArrayString();\n"
    "    }\n"
    "    function GetModuleMenu(who,data) {\n"
    "        return wxArrayString();\n"
    "    }\n"
    "    function Execute() {\n"
    "        LogDebug(info.name + _T(\"::Run() : 未实现\"));\n"
    "        return -1;\n"
    "    }\n"
    "    function OnMenuClicked(index) {\n"
    "        LogDebug(info.name + _T(\": 点击菜单: \") + index);\n"
    "    }\n"
    "    function OnModuleMenuClicked(index) {\n"
    "        LogDebug(info.name + _T(\": 已单击模块菜单: \") + index);\n"
    "    }\n"
    "}\n";
  void Register_ScriptPlugin() {
    SqPlus::RegisterGlobal( &ScriptPluginWrapper::ExecutePlugin, "ExecutePlugin" );
    SquirrelVM::CreateFunctionGlobal( &ScriptPluginWrapper::GetPlugin, "GetPlugin", "*" );
    SquirrelVM::CreateFunctionGlobal( &ScriptPluginWrapper::RegisterPlugin, "RegisterPlugin", "*" );
    SQPRINTFUNCTION oldPrintFunc = sq_getprintfunc( SquirrelVM::GetVMPtr() );
    sq_setprintfunc( SquirrelVM::GetVMPtr(), 0 );
    SquirrelObject script;
    try {
      script = SquirrelVM::CompileBuffer( s_cbScriptPlugin, "cbScriptPlugin" );
      SquirrelVM::RunScript( script );
    } catch( SquirrelError e ) {
      msging( F( _T( "注册脚本插件框架失败.\n\n%s" ), cbC2U( e.desc ).c_str() ),
              _T( "脚本编译错误" ), wxICON_ERROR );
    }
    sq_setprintfunc( SquirrelVM::GetVMPtr(), oldPrintFunc );
  }

};

